package com.example.demo;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.demo.seckill.Application;
import com.demo.seckill.distributedlock.redis.RedissLockUtil;
/**
 * @author libo
 * @ClassName RedissonLockDemo
 * @Description: 分布式锁demo
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class,webEnvironment = SpringBootTest.WebEnvironment.NONE)
@WebAppConfiguration
public class RedissonLockDemo {

    /***
     * @Author libo
     * @Description // Redisson可重入锁测试，实现了java对象的lock接口
     * @return
     */
    @Test
    public void testReentrantLock() throws InterruptedException {
        RLock lock =  RedissLockUtil.getLock("lockKey");
        for (int i=0;i<1;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 1. 最常见的使用方法
                     //lock.lock();
                    // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
                    // lock.lock(10, TimeUnit.SECONDS);
                    // 3. 尝试加锁，最多等待3秒，上锁以后10秒自动解锁
                    try {
                        boolean res = lock.tryLock(10, 1000, TimeUnit.SECONDS);
                        if(res){
                            System.out.println(Thread.currentThread().getName()+"线程获取锁成功");
                            //获得锁，执行业务
                        }else {
                            System.out.println(Thread.currentThread().getName()+"线程获取锁失败");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        //过期自动解锁,无需手动解锁
        //lock.unlock();
        Thread.currentThread().join(10000);
    }
    /***
     * @Author libo
     * @Description // 为分布式锁提供异步执行的方法
     * @return void
     */
    @Test
    public void testAsyncReentrantLock() throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    /*lock.tryLockAsync();
                    加锁10后自动解锁
                    lock.tryLockAsync(10, TimeUnit.SECONDS);
                    lock.tryLockAsync(3, 20, TimeUnit.SECONDS)*/
                    RFuture <Boolean>  res  = RedissLockUtil.tryLockAsync("getLock",3, 20, TimeUnit.SECONDS);
                    if(res.get()){
                        System.out.println("这个是获取到锁之后，异步调用的方法");
                    }
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        System.out.println("这是主线程的方法");
        Thread.sleep(10000);
    }

    /**
     * 公平锁（Fair Lock）
     * Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。
     * 在提供了自动过期解锁功能的同时，保证了当多个Redisson客户端线程同时请求加锁时，优先分配给先发出请求的线程。
     * @param redisson
     */

    @Test
    public void testFairLock() throws InterruptedException {
        final RLock fairLock = RedissLockUtil.getFairLock("getLock");
        new Thread(new MyRunner("线程A",fairLock)).start();
        new Thread(new MyRunner("线程B",fairLock)).start();
        new Thread(new MyRunner("线程C",fairLock)).start();
        Thread.sleep(100000);
    }

    /**
     * 联锁（MultiLock）
     * Redisson的RedissonMultiLock对象可以将多个RLock对象关联为一个联锁，
     * 每个RLock对象实例可以来自于不同的Redisson实例
     */
    @Test
    public void testMultiLock(){
        RLock lock1 = RedissLockUtil.getLock("lock1");
        RLock lock2 = RedissLockUtil.getLock("lock2");
        RLock lock3 = RedissLockUtil.getLock("lock3");
        RedissonMultiLock multiLock = new RedissonMultiLock(lock1,lock2,lock3);
        try{
            //multiLock.lock();
            //multiLock.lock(100,TimeUnit.SECONDS);
            boolean res = multiLock.tryLock(10, 10, TimeUnit.SECONDS);
            if(res){
                System.out.println("这是联锁测试");
            }
        }catch (Exception e){
            System.out.println("获取不到锁，发生异常");
            e.printStackTrace();
        }finally {
            try{
                multiLock.unlock();
            }catch (Exception e){
                System.out.println("获取不到锁，解锁发生异常");
            }
        }
    }

    @Test
    public void testAlwaysLock() throws InterruptedException {
        RLock lock =  RedissLockUtil.getLock("lock1");
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 1. 最常见的使用方法
                // lock.lock();
                // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
                // lock.lock(10, TimeUnit.SECONDS);
                // 3. 尝试加锁，最多等待3秒，上锁以后10秒自动解锁
                try {
                    boolean res = lock.tryLock();
                    if(res){
                        System.out.println(Thread.currentThread().getName()+"线程获取锁成功");
                        //获得锁，执行业务
                    }else {
                        System.out.println(Thread.currentThread().getName()+"线程获取锁失败");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        Thread.sleep(1000000);
    }
    /**
     *  红锁（RedLock）
     *  Redisson的RedissonRedLock对象实现了Redlock介绍的加锁算法。
     *  该对象也可以用来将多个RLock对象关联为一个红锁，每个RLock对象实例可以来自于不同的Redisson实例
     */
    @Test
    public void testRedLock(){
        RLock lock1 = RedissLockUtil.getLock("lock1");
        RLock lock2 = RedissLockUtil.getLock("lock2");
        RLock lock3 = RedissLockUtil.getLock("lock3");
        RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
        try {
            // 同时加锁：lock1 lock2 lock3, 红锁在大部分节点上加锁成功就算成功。
            lock.lock();
            // 尝试加锁，最多等待100秒，上锁以后10秒自动解锁
            boolean res = lock.tryLock(10, 10, TimeUnit.SECONDS);
            if (res) {
                System.out.println("这是红锁测试");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /***
     * @Author libo
     * @Description //读写锁测试
     * @return
     */
    @Test
    public void testRWlock() throws InterruptedException {
                        RReadWriteLock rwlock = RedissLockUtil.getReadWriteLock("lockKey");
                        for (int i = 0; i < 3; i++) {
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        boolean redRes = rwlock.readLock().tryLock(5, 10, TimeUnit.SECONDS);
                                        if(redRes){
                                            System.out.println(Thread.currentThread().getName()+"线程获取读锁成功");
                                        }else{
                                            System.out.println(Thread.currentThread().getName()+"线程获取读锁失败");
                                        }
                                        boolean writeRes = rwlock.writeLock().tryLock(10, 10, TimeUnit.SECONDS);
                                        if (writeRes){
                                            System.out.println(Thread.currentThread().getName()+"线程获取写锁成功");
                                        }else {
                                            System.out.println(Thread.currentThread().getName()+"线程获取写锁失败");
                                        }
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                }
            }).start();
        }
        Thread.sleep(100000);
    }

    /***
     * @Author libo
     * @Description //信号量测试
     * @return
     */
    @Test
    public void testSemaphore() throws InterruptedException {
        RSemaphore semaphore = RedissLockUtil.getSemaphore("semaphore ");
        semaphore.trySetPermits(10);
        Demo demo = new Demo(semaphore);
        for (int i = 0; i < 5; i++) {
            (new MyThread(demo)).start();
        }
        Thread.sleep(10000);
    }

    /***
     * @Author libo
     * @Description //RCountDownLatch测试
     * @return
     */
    @Test
    public void testCountDownLatch() throws InterruptedException {
        int threadCount = 1;
        final RCountDownLatch countDownlatch = RedissLockUtil.getCountDownLatch("countDownlatch ");
        countDownlatch.trySetCount(threadCount);
        Demo demo = new Demo(countDownlatch);
        for (int i = 0; i < 3; i++) {
            (new MyThread(demo)).start();
        }
        System.out.println("主线程阻塞,等待所有子线程执行完成");
        //countDownlatch.await()使得主线程阻塞直到countDownlatch.countDown()为零才继续执行
        countDownlatch.await();
        System.out.println("所有线程执行完成!");
        Thread.sleep(10000);
    }
    class  MyRunner implements Runnable{
        String threadName;
        RLock rLock;

        public MyRunner(String threadName,RLock rLock){
            this.threadName = threadName;
            this.rLock = rLock;
        }

        public String getThreadName() {
            return threadName;
        }

        public void setThreadName(String threadName) {
            this.threadName = threadName;
        }

        public RLock getrLock() {
            return rLock;
        }

        public void setrLock(RLock rLock) {
            this.rLock = rLock;
        }
        @Override
        public void run() {
            try{
                System.out.println("线程名称:"+getThreadName()+"stating ");
                /*
                fairLock.lock();
                // 支持过期解锁功能, 10秒钟以后自动解锁,无需调用unlock方法手动解锁
                fairLock.lock(10, TimeUnit.SECONDS);*/
                // 尝试加锁，最多等待100秒，上锁以后10秒自动解锁
                boolean res = rLock.tryLock(100, 10, TimeUnit.SECONDS);
                if (res) {
                    System.out.println("线程名称:"+getThreadName()+"获取锁成功");
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

     class Demo {
        private RSemaphore semaphore;
        private RCountDownLatch rCountDownLatch;

        public Demo(RSemaphore semaphore){
            this.semaphore = semaphore;
        }
        public Demo(RCountDownLatch rCountDownLatch){
            this.rCountDownLatch = rCountDownLatch;
        }
        public void test1() {
            try {
                // 从信号量中获取一个允许机会
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + " start at " + System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + " stop at " + System.currentTimeMillis());
                // 释放允许，将占有的信号量归还
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
         public void test2() {
            try{
                System.out.println( Thread.currentThread().getName() +" start at "+ System.currentTimeMillis());
                // 每个独立子线程执行完后,countDownLatch值减1
                Thread.sleep(1000);
                rCountDownLatch.countDown();
            }catch (Exception e){
                e.printStackTrace();
            }
         }
    }
    class MyThread extends Thread{
        private Demo demo;

        public MyThread(Demo demo) {
            super();
            this.demo = demo;
        }

        public void run() {
            //demo.test1();
            demo.test2();
        }
    }
}
